package pers.hl.library.common.base;

import org.apache.log4j.Logger;
import pers.hl.library.common.Const;
import pers.hl.library.utils.LogUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.List;

/**
 *
 * @param <T> 实体类，对应数据库表
 * @param <E> 对应Example类
 */
public abstract class BaseService<T, E extends IExample> implements IService<T> {

    protected Logger logger = Logger.getLogger(this.getClass().getName());
    
    protected final List<T> EMPTY_LIST = Collections.emptyList();

    /** 默认调用构造方法生成一个实例  */
    protected E mExample;

    /**
     * example的Class对象
     */
    private Class<E> exampleClazz;

    /**
     * dao层实例
     */
    protected IMapper<T, E> dao;

    public BaseService(){
        LogUtils.i("instantiate service:"+getClass().getSimpleName());
        LogUtils.i("instantiate Example:"+ getRealExampleClassType().getSimpleName());
        Constructor<E> constructor;
        try {
            constructor = exampleClazz.getConstructor();
            constructor.setAccessible(true);
            mExample = constructor.newInstance();
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 在子类构造函数中通过依赖注入，指明具体的mapper层
     *
     * @param mapper 统一mapper
     */
    public void init(IMapper<T, E> mapper) {
        this.dao = mapper;
    }

    // 使用反射技术得到E的真实类型
    @SuppressWarnings("unchecked")
    public Class<E> getRealExampleClassType(){
        // 获取当前new的对象的泛型的父类类型
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        // 获取第二个类型参数的真实类型
        this.exampleClazz = (Class<E>) pt.getActualTypeArguments()[1];
        return exampleClazz;
    }

    protected E getExample() {
        return mExample;
    }

    protected E createExample() {
        Constructor<E> constructor;
        try {
            constructor = exampleClazz.getConstructor();
            constructor.setAccessible(true);
            mExample = constructor.newInstance();
            return mExample;
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean addData(T data) {
        return dao.insert(data) > 0;
    }

    @Override
    public boolean addDataSelective(T data) {
        return dao.insertSelective(data) > 0;
    }

    @Override
    public boolean batchAdd(List<T> list) {
        return dao.batchAdd(list) > 0;
    }

    @Override
    public boolean deleteData(Integer id) {
        if (dao.deleteByPrimaryKey(id) > 0) {
            return afterDelete(id);
        }
        return false;
    }

    protected boolean afterDelete(Integer id) {
        return true;
    }

    @Override
    public boolean batchDelete(List<Integer> ids) {
        if (dao.batchDelete(ids) > 0) {
            return afterBatchDelete(ids);
        }
        return false;
    }

    protected boolean afterBatchDelete(List<Integer> ids) {
        return true;
    }

    @Override
    public boolean update(T data) {
        return dao.updateByPrimaryKey(data) > 0;
    }

    @Override
    public boolean updateSelective(T data) {
        return dao.updateByPrimaryKeySelective(data) > 0;
    }

    @Override
    public boolean updateByExample(T data) {
        return dao.updateByExample(data, mExample) > 0;
    }

    @Override
    public boolean updateByExampleSelective(T data) {
        return dao.updateByExampleSelective(data, mExample) > 0;
    }

    @Override
    public T getDataByPrimaryId(Integer id) {
        return dao.selectByPrimaryKey(id);
    }

    @Override
    public List<T> getDataList(Integer id) {
        return EMPTY_LIST;
    }

    @Override
    public List<T> getDataList(String key, Integer otherId) {
        return EMPTY_LIST;
    }

    @Override
    public List<T> getDataList2(String key, String value) {
        switch (key) {
            case Const.RequestKey.KEY_ALL:
                return dao.selectByExample(createExample());
            default:
                break;
        }
        return EMPTY_LIST;
    }

    @Override
    public T getDataByOtherId(Integer otherId) {
        return dao.getDataByPrimaryOtherId(otherId);
    }


}
